package com.calt.plm.basic.core.model;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.ibatis.jdbc.SQL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.calt.plm.basic.core.action.Action;
import com.calt.plm.basic.core.annoation.BusinessAction;
import com.calt.plm.basic.core.annoation.Prop;
import com.calt.plm.basic.core.factory.SqlEngine;
import com.calt.plm.basic.core.reader.ActionReader;
import com.calt.plm.basic.core.util.ClassUtil;

/**
 * <p>类的简单描述</p>
 *
 * @author wuchao Create on 2022/9/22
 * @version 1.0
 */
public class EntityDescribe
{
	private static final Logger LOGGER = LoggerFactory.getLogger(EntityDescribe.class);

	private Class clazz;

	private String entityName;

	private String tableName;

	private DataSource ds;

	private Map<String, FieldWrapper> fieldMapping;

	private Map<String, FieldWrapper> columnMapping;

	private Set<String> primaryColumn = new LinkedHashSet<>();

	private Set<String> normalColumn = new LinkedHashSet<>();

	private Set<String> allColumn = new HashSet<>();

	private String insertSql;

	private String updateSql;

	private String deleteSql;

	private String deleteByPkSql;

	private String querySql;

	private String countSql;

	public EntityDescribe(Class clazz, String name, DataSource dataSource)
	{
		this.tableName = name;
		this.ds = dataSource;
		this.entityName = ClassUtils.getShortClassName(clazz);
		this.clazz = clazz;
		this.fieldMapping = new HashMap<>();
		this.columnMapping = new HashMap<>();
		this.init();
	}

	public void init()
	{
		List<ActionProp> props = this.initEntityClass(this.clazz);
		new SqlEngine().createTable(this.tableName, this.ds, props);
		this.initColumn();
		this.initSql();
	}

	protected List<ActionProp> initEntityClass(Class clazz)
	{
		List<Class<?>> interfaces = ClassUtil.getAllInterfaces(clazz);
		List<ActionProp> props = new ArrayList<>();
		for (Class<?> anInterface : interfaces)
		{
			if(ClassUtil.isAssignable(anInterface, Action.class))
			{
				BusinessAction annotation = anInterface.getAnnotation(BusinessAction.class);
				if(null == annotation)
				{
					continue;
				}
				ActionDefine actionDefine = ActionReader.get(annotation.name());
				if(null == actionDefine)
				{
					continue;
				}
				List<ActionProp> propList = actionDefine.getPropList();
				if(CollectionUtils.isEmpty(propList))
				{
					continue;
				}
                props.addAll(propList);
                for (ActionProp actionProp : propList)
                {
                    fieldMapping.put(actionProp.getName(), new FieldWrapper(actionProp.getName()));
                    columnMapping.put(actionProp.getName(), new FieldWrapper(actionProp.getName()));
                }
			}
		}
		List<Field> allField = ClassUtil.getAllField(clazz);
		FieldWrapper fieldWrapper;
		for (Field field : allField)
		{
			fieldWrapper = new FieldWrapper(field.getName(), field);
			fieldMapping.put(field.getName(), fieldWrapper);
			Prop annotation = field.getAnnotation(Prop.class);
			if(null == annotation)
			{
				continue;
			}
            fieldWrapper = new FieldWrapper(annotation.name(), field);
			props.add(new ActionProp(annotation.name(), annotation.type(), annotation.desc(), annotation.length(),
					annotation.precision(), annotation.index()));
			columnMapping.put(annotation.name(), fieldWrapper);
		}
		return props;
	}

	protected void initColumn()
	{
		// 测试代码
		this.primaryColumn.add("id");
		this.normalColumn.add("folder_id");
		this.normalColumn.add("part_code");
		this.normalColumn.add("part_price");
		this.normalColumn.add("production_time");

		this.normalColumn.add("create_by");
		this.normalColumn.add("update_by");
		this.normalColumn.add("bizVersion");
		this.normalColumn.add("policy_id");
		this.normalColumn.add("bizCurrent");
		this.normalColumn.add("avariable");
		this.normalColumn.add("containerId");
		this.normalColumn.add("teamId");

		this.allColumn.addAll(this.primaryColumn);
		this.allColumn.addAll(this.normalColumn);

		/*Connection connection = null;
		ResultSet primaryKeyResultSet = null;
		ResultSet normalColumnResultSet = null;
		try
		{
			connection = this.ds.getConnection();
			DatabaseMetaData metaData = connection.getMetaData();
			String driverName = metaData.getDriverName();
			primaryKeyResultSet = metaData.getPrimaryKeys(connection.getCatalog(), metaData.getUserName(),
					this.tableName);
			while (null != primaryKeyResultSet && primaryKeyResultSet.next())
			{
				String columnName = primaryKeyResultSet.getString("COLUMN_NAME");
				primaryColumn.add(columnName);
				allColumn.add(columnName);
			}
			normalColumnResultSet = metaData.getColumns(connection.getCatalog(), metaData.getUserName(), this.tableName,
					null);
			while (null != normalColumnResultSet && normalColumnResultSet.next())
			{
				String columnName = normalColumnResultSet.getString("COLUMN_NAME");
				if(primaryColumn.contains(columnName))
				{
					continue;
				}
				normalColumn.add(columnName);
				allColumn.add(columnName);
			}
		}
		catch (Exception e)
		{
			LOGGER.error("init analyze table " + this.tableName + " error", e);
			throw new RuntimeException(e);
		}
		finally
		{
			JdbcUtils.closeResultSet(primaryKeyResultSet);
			JdbcUtils.closeResultSet(normalColumnResultSet);
			JdbcUtils.closeConnection(connection);
		}
		if(CollectionUtils.isEmpty(this.primaryColumn))
		{
			throw new RuntimeException(this.tableName + " is not has primary column,entity not init");
		}*/
	}

	protected void initSql()
	{
		initInsertSql();
		initUpdateSql();
		initDeleteSql();
		initQuerySql();
		initCountSql();
	}

	private void initInsertSql()
	{
		SQL sql = new SQL().INSERT_INTO(this.tableName);
		Collection<FieldWrapper> fieldWrappers = this.columnMapping.values();
		for (FieldWrapper fieldWrapper : fieldWrappers)
		{
			String columnName = fieldWrapper.getColumnName();
			String fieldName = fieldWrapper.getFieldName();
			sql.VALUES(columnName, ":" + fieldName);
		}
		this.insertSql = sql.toString();
	}

	private void initUpdateSql()
	{
		SQL sql = new SQL().UPDATE(this.tableName);
		FieldWrapper fieldWrapper;
		for (String column : normalColumn)
		{
			fieldWrapper = this.getFieldWrapperByColumn(column);
			if(null != fieldWrapper)
			{
				sql.SET(fieldWrapper.getSqlPart());
			}
		}
		for (String column : primaryColumn)
		{
			fieldWrapper = this.getFieldWrapperByColumn(column);
			if(null != fieldWrapper)
			{
				sql.WHERE(fieldWrapper.getSqlPart());
			}
		}
		this.updateSql = sql.toString();
	}

	private void initDeleteSql()
	{
		SQL sql = new SQL().DELETE_FROM(this.tableName);
		this.deleteSql = sql.toString();

		FieldWrapper fieldWrapper;
		for (String column : primaryColumn)
		{
			fieldWrapper = this.getFieldWrapperByColumn(column);
			if(null != fieldWrapper)
			{
				sql.WHERE(fieldWrapper.getSqlPart());
			}
		}
		this.deleteByPkSql = sql.toString();
	}

	private void initQuerySql()
	{
		SQL sql = new SQL();
		for (String column : allColumn)
		{
			sql.SELECT(column);
		}
		sql.FROM(this.tableName);
		this.querySql = sql.toString();
	}

	private void initCountSql()
	{
		SQL sql = new SQL().SELECT("COUNT(1) AS COUNT");
		sql.FROM(this.tableName);
		this.countSql = sql.toString();
	}

	public FieldWrapper getFieldWrapper(String filedName)
	{
		return fieldMapping.get(filedName);
	}

	public FieldWrapper getFieldWrapperByColumn(String columnName)
	{
		return columnMapping.get(columnName);
	}

	public Class getClazz()
	{
		return clazz;
	}

	public String getEntityName()
	{
		return entityName;
	}

	public String getTableName()
	{
		return tableName;
	}

	public DataSource getDs()
	{
		return ds;
	}

	public Map<String, FieldWrapper> getFieldMapping()
	{
		return fieldMapping;
	}

	public Map<String, FieldWrapper> getColumnMapping()
	{
		return columnMapping;
	}

	public Set<String> getPrimaryColumn()
	{
		return primaryColumn;
	}

	public Set<String> getNormalColumn()
	{
		return normalColumn;
	}

	public Set<String> getAllColumn()
	{
		return allColumn;
	}

	public String getInsertSql()
	{
		return insertSql;
	}

	public String getUpdateSql()
	{
		return updateSql;
	}

	public String getDeleteSql()
	{
		return deleteSql;
	}

	public String getDeleteByPkSql()
	{
		return deleteByPkSql;
	}

	public String getQuerySql()
	{
		return querySql;
	}

	public String getCountSql()
	{
		return countSql;
	}
}
